#ifndef VTKVELODYNEBASEPACKETINTERPRETER_H
#define VTKVELODYNEBASEPACKETINTERPRETER_H

#include "vtkLidarPacketInterpreter.h"
#include "VelodyneInterpreterCommon.h"

#include <vtkSetGet.h>
#include<vtkDoubleArray.h>

#include "vtkDataPacket.h"
using namespace DataPacketFixedLength;

#define IMPL_GET_LASER_CORRECTION(ARRAY_NAME) vtkDoubleArray* Get_ ## ARRAY_NAME () \
{ \
        this->ARRAY_NAME ## Interface->SetNumberOfTuples(HDL_MAX_NUM_LASERS); \
        for (int i = 0; i < HDL_MAX_NUM_LASERS; ++i) \
        { \
          this->ARRAY_NAME ## Interface->SetTuple1(i, this->laser_corrections_[i].ARRAY_NAME); \
        } \
        return ARRAY_NAME ## Interface.GetPointer(); \
}

//------------------------------------------------------------------------------
// VelodyneBasePacketInterpreter
//------------------------------------------------------------------------------
class vtkRollingDataAccumulator;

class VTK_EXPORT vtkVelodyneBasePacketInterpreter : public vtkLidarPacketInterpreter
{
public:
  vtkTypeMacro(vtkVelodyneBasePacketInterpreter, vtkLidarPacketInterpreter);
  void PrintSelf(ostream& vtkNotUsed(os), vtkIndent vtkNotUsed(indent)){};

protected:
  vtkVelodyneBasePacketInterpreter();
  ~vtkVelodyneBasePacketInterpreter() {};

private:
  vtkVelodyneBasePacketInterpreter(const vtkVelodyneBasePacketInterpreter&) = delete;
  void operator=(const vtkVelodyneBasePacketInterpreter&) = delete;

protected:
  bool IsCorrectionFromLiveStream { false };
  unsigned char SensorPowerMode { 0 };
  DualReturnSensorMode ReportedSensorReturnMode;

  // Framing logic to use if the property is not read from the calibration file
  // We choose FL_AZIMUTH_CROSSING because this is the "legacy" framing logic,
  // so the most likely one to be required.
  FramingLogic FrameLogic  { FramingLogic::FL_AZIMUTH_CROSSING };

private:
  std::vector<double> cos_lookup_table_;
  std::vector<double> sin_lookup_table_;
  HDLLaserCorrection laser_corrections_[HDL_MAX_NUM_LASERS];

  double XMLColorTable[HDL_MAX_NUM_LASERS][3];


public:
	void InitTrigonometricTables();
  void PrecomputeCorrectionCosSin();

  // This may modify this->FrameLogic, if file contains "frameLogic" field.
  virtual void LoadCalibration(std::string const & filename) override;

  /*!
   * @brief      Compute corrected position and other values using the Velodyne
   *             calibration file.
   * @param[in]  rawValues        The raw input values.
   * @param[in]  correctionIndex  The index of the laser to be retrieved from
   *                              the calibration file.
   * @param[out] correctedValues  The computed corrected values.
   * @param[in]  correctIntensity If true, correct the intensity.
   */
  void ComputeCorrectedValues(
    const RawValues & rawValues,
    const unsigned int correctionIndex,
    CorrectedValues & correctedValues,
    bool correctIntensity = false
  );

  bool HDL64LoadCorrectionsFromStreamData(vtkRollingDataAccumulator * rollingCalibrationData);

  void GetXMLColorTable(double XMLColorTable[4 * HDL_MAX_NUM_LASERS]);
  
  void GetLaserCorrections(double verticalCorrection[HDL_MAX_NUM_LASERS],
    double rotationalCorrection[HDL_MAX_NUM_LASERS], double distanceCorrection[HDL_MAX_NUM_LASERS],
    double distanceCorrectionX[HDL_MAX_NUM_LASERS], double distanceCorrectionY[HDL_MAX_NUM_LASERS],
    double verticalOffsetCorrection[HDL_MAX_NUM_LASERS],
    double horizontalOffsetCorrection[HDL_MAX_NUM_LASERS], double focalDistance[HDL_MAX_NUM_LASERS],
    double focalSlope[HDL_MAX_NUM_LASERS], double minIntensity[HDL_MAX_NUM_LASERS],
    double maxIntensity[HDL_MAX_NUM_LASERS]);


  FramingLogic GetFramingLogic() { return this->FrameLogic; }

  // Legacy code.
protected:
  uint8_t ReportedFactoryField1 ;
  uint8_t ReportedFactoryField2 ;
  bool OutputPacketProcessingDebugInfo ;
  bool UseIntraFiringAdjustment;
  unsigned int DualReturnFilter ;
  int FiringsSkip;
  bool SkipExtDataBlock;
  bool IsHDL64Data ;
  bool HasDualReturn ;
  bool ShouldAddDualReturnArray;
  bool WantIntensityCorrection;
  bool HideDropPoints = false;

  // These arrays are used to pass data to the client side (applogic.py)
  vtkNew<vtkDoubleArray> verticalCorrectionInterface;
  vtkNew<vtkDoubleArray> rotationalCorrectionInterface;
  vtkNew<vtkDoubleArray> distanceCorrectionInterface;
  vtkNew<vtkDoubleArray> distanceCorrectionXInterface;
  vtkNew<vtkDoubleArray> distanceCorrectionYInterface;
  vtkNew<vtkDoubleArray> verticalOffsetCorrectionInterface;
  vtkNew<vtkDoubleArray> horizontalOffsetCorrectionInterface;
  vtkNew<vtkDoubleArray> focalDistanceInterface;
  vtkNew<vtkDoubleArray> focalSlopeInterface;
  vtkNew<vtkDoubleArray> minIntensityInterface;
  vtkNew<vtkDoubleArray> maxIntensityInterface;

public:
  vtkSetMacro(UseIntraFiringAdjustment, bool)
  vtkGetMacro(UseIntraFiringAdjustment, bool)
  
  vtkSetMacro(DualReturnFilter, bool)
  vtkGetMacro(DualReturnFilter, bool)

  vtkSetMacro(FiringsSkip, int)
  vtkGetMacro(FiringsSkip, int)

  vtkGetMacro(HasDualReturn, bool)

  vtkSetMacro(WantIntensityCorrection, bool)
  vtkGetMacro(WantIntensityCorrection, bool)

  vtkSetMacro(HideDropPoints, bool)
  vtkGetMacro(HideDropPoints, bool)

  IMPL_GET_LASER_CORRECTION(verticalCorrection)
  IMPL_GET_LASER_CORRECTION(rotationalCorrection)
  IMPL_GET_LASER_CORRECTION(distanceCorrection)
  IMPL_GET_LASER_CORRECTION(distanceCorrectionX)
  IMPL_GET_LASER_CORRECTION(distanceCorrectionY)
  IMPL_GET_LASER_CORRECTION(verticalOffsetCorrection)
  IMPL_GET_LASER_CORRECTION(horizontalOffsetCorrection)
  IMPL_GET_LASER_CORRECTION(focalDistance)
  IMPL_GET_LASER_CORRECTION(focalSlope)
  IMPL_GET_LASER_CORRECTION(minIntensity)
  IMPL_GET_LASER_CORRECTION(maxIntensity)

  std::string GetDefaultRecordFileName() override;

  /**
   * @brief GetSensorName return the name of the lidar sensor used
   */
  virtual std::string GetSensorName() = 0;
};

#endif // VTKVELODYNEBASEPACKETINTERPRETER_H

